﻿using BCCommon;
using BCCommon.Enums;
using BCCommon.Exceptions;
using BCCommon.Extensions;
using BCCommon.PaymentEnums;
using BCData.Common.FlowRecord;
using BCData.Common.Payment.Payment;
using BCData.Common.SystemFinanceFlowRecord;
using BCData.Common.Wallet;
using BCData.Website.User.User;
using BCDto.Common.Payment.Payment;
using BCDto.Common.Payment.PaymentRecord;
using BCDto.Common.Wallet;
using BCDto.Sim.Admin.Admin;
using BCDto.Website.User;
using BCEntity.Common.FlowRecord;
using BCEntity.Common.Payment.Payment;
using BCEntity.Common.SystemFinanceFlowRecord;
using BCService.Common.ExceptionService;
using BCService.Common.Wallet;
using BCService.TMS.AgentWallet;
using BCService.Utils;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace BCService.Common.Payment.Payment
{
    public class BalancePayService : IBalancePayService
    {
        
        private const string SIGNATURE_SALT = "Hello@1234~";

        private readonly IConfiguration configuration;
        private readonly IPaymentRecordData paymentRecordData;
        private readonly IUserData userData;
        private readonly IWalletData walletData;
        private readonly IUserFlowRecordData userFlowRecordData;
        private readonly ISystemFinanceFlowRecordData systemFinanceFlowRecordData;
        private readonly IWalletService walletService;
        private readonly ISystemExceptionRecordService exceptionRecordService;
        private readonly IAgentWalletService agentWalletService;
        public PaymentChannelType ChannelType { get; set; }

        public SourceAppType AppType { get; set; }

        public SourceOSType OSType { get; set; }

        public BalancePayService(IConfiguration configuration,
            IPaymentRecordData paymentRecordData,
            IUserData userData,
            IWalletData walletData,
            IUserFlowRecordData userFlowRecordData,
            ISystemFinanceFlowRecordData systemFinanceFlowRecordData,
            IWalletService walletService,
            ISystemExceptionRecordService exceptionRecordService,
            IAgentWalletService agentWalletService)
        {
            this.configuration = configuration;
            this.paymentRecordData = paymentRecordData;
            this.userData = userData;
            this.walletData = walletData;
            this.userFlowRecordData = userFlowRecordData;
            this.systemFinanceFlowRecordData = systemFinanceFlowRecordData;
            this.walletService = walletService;
            this.exceptionRecordService = exceptionRecordService;
            this.agentWalletService = agentWalletService;
        }

        public decimal? GetBalance(UserDto userDto)
        {
            var wallet = this.walletService.GetWalletByUserId(userDto.UserId);

            return wallet?.Balance ?? 0M;
        }

        public decimal? GetLimit(UserDto userDto)
        {
            return this.GetBalance(userDto);
        }

        public decimal? GetAgentBalance(long agentRecordId)
        {
            var agentWallet = this.agentWalletService.GetWalletByAgentId(agentRecordId);

            return agentWallet?.Balance ?? 0M;
        }

        public decimal? GetAgentLimit(long agentRecordId)
        {
            return this.GetAgentBalance(agentRecordId);
        }

        public PaymentDto TryPay(SourceAppType appType, FlowType orderType, string subject, string referenceNumber, SourceOSType osType, SourceDeviceType deviceType, string deviceId, IDictionary<string, string> parameters, decimal amount, SourcePayerType? payer,UserDto userDto)
        {
            var password = parameters.FirstOrDefault(p => p.Key.ToUpperInvariant() == "PASSWORD").Value;

            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentException("当前支付方式需要验证密码，请输入支付密码。");
            }

            var paymentRecord = this.paymentRecordData.Create(this.ChannelType, orderType.ToString(),subject, referenceNumber, Math.Round(amount, 2), userDto.UserId, appType, osType, payer, $"{this.ChannelType}-{this.AppType}");

            try
            {
                var wallet = this.walletData.GetWalletByUserId(userDto.UserId);

                if (wallet == null)
                {
                    throw new Exception("用户钱包不存在。");
                }

                if (string.IsNullOrEmpty(wallet.PayPassword))
                {
                    throw new ArgumentException("未设置支付密码，无法完成支付。");
                }

                if (wallet.PasswordErrorCount >= 3)
                {
                    throw new ArgumentException("输入密码错误次数太多，请稍后再试。");
                }

                var user = this.userData.GetUserByUserId(userDto.UserId).Result;

                if (user?.AccountStatus != AccountStatus.Using)
                {
                    throw new ArgumentException("用户不存在或者已禁用，无法使用余额支付。");
                }

                if (password.SHA256(user.Phone).ToUpperInvariant() != wallet.PayPassword.ToUpperInvariant())
                {
                    this.walletData.UpdatePasswordErrorCount(user.UserId, wallet.PasswordErrorCount++);

                    throw new ArgumentException("支付密码错误，请重试。");
                }

                if (wallet.Balance < paymentRecord.Amount)
                {
                    throw new ArgumentException("您的可用余额不足，请充值后再试或者选择其他支付方式。");
                }

                // 更新钱包余额
                wallet.Balance = wallet.Balance - paymentRecord.Amount;

                // 钱包流水
                var userFlowRecord = new UserFlowRecordEntity
                {
                    FlowType = orderType.ToString().AsEnum(FlowType.Other),
                    OrderNo = referenceNumber,
                    Balance = wallet.Balance,
                    FlowDirection = SourceFlowDirectionType.Outlay,
                    Amount = -paymentRecord.Amount,
                    CreateTime = DateTime.Now,
                    Remark = $"{orderType.GetDisplayString()} {paymentRecord.Amount:n2} 元",
                    Channel = this.ChannelType,
                    UserId = user.UserId,
                    UserName = user.UserName
                };

                // 系统流水
                var systemFinanceFlowRecord = new SystemFinanceFlowRecordEntity
                {
                    FlowType = orderType.ToString().AsEnum(FlowType.Other),
                    OrderNo = referenceNumber,
                    FlowDirection = SourceFlowDirectionType.Outlay,
                    Amount = -paymentRecord.Amount,
                    Remark = $"{orderType.GetDisplayString()} {paymentRecord.Amount:n2} 元",
                    Channel = this.ChannelType,
                    UserId = user.UserId,
                    UserName = user.UserName
                };

                // TODO: 合并事务!
                this.walletData.Update(wallet);
                this.userFlowRecordData.AddAsync(userFlowRecord).GetAwaiter().GetResult();
                this.systemFinanceFlowRecordData.Append(systemFinanceFlowRecord).GetAwaiter().GetResult();

                // 支付成功
                paymentRecord = this.paymentRecordData.SetAsSuccess(paymentRecord.PaymentRecordId);

                string paymentRecordId = paymentRecord.PaymentRecordId.ToString();
                string _referenceNumber = referenceNumber;
                string originalAmount = amount.ToString();
                string discount = (amount - paymentRecord.Amount).ToString();
                string paidAmount = paymentRecord.Amount.ToString();
                string status = paymentRecord.Status;

                string arguments = $"PaymentRecordId={paymentRecordId}&ReferenceNumber={referenceNumber}&OriginalAmount={originalAmount}&Discount={discount}&PaidAmount={paidAmount}&Status={status}&Signature={HashHelper.SHA256(paymentRecordId + _referenceNumber + originalAmount + discount + paidAmount + status, SIGNATURE_SALT)}";

                PerformanceTracer.InvokeAsync(delegate
                {
                    string callbackUrl = string.Format(configuration["PayNotify:postUrl"], this.ChannelType, userDto.UserId);

                    try
                    {
                        new WebClient().DownloadString($"{callbackUrl}?{arguments}");
                    }
                    catch (Exception ex)
                    {
                        this.exceptionRecordService.InsertException(ex, JsonConvert.SerializeObject(paymentRecord));
                    }
                });

                return paymentRecord.TransformTo<PaymentDto>(onAfter: (_, dest) =>
                {
                    dest.Parameters = new Dictionary<string, string>
                    {
                        { "Balance", wallet.Balance.ToString() }
                    };
                    dest.ActionArguments = arguments;
                });
            }
            catch (Exception ex)
            {
                this.paymentRecordData.SetAsFailed(paymentRecord.PaymentRecordId, ex.Message);

                throw;
            }
        }

        public PaymentDto AgentTryPay(SourceAppType appType, FlowType orderType, string subject, string referenceNumber, SourceOSType osType,
            SourceDeviceType deviceType, string deviceId, IDictionary<string, string> parameters, decimal amount, SourcePayerType? payer,
            AdminDto admin)
        {
            var password = parameters.FirstOrDefault(p => p.Key.ToUpperInvariant() == "PASSWORD").Value;

            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentException("当前支付方式需要验证密码，请输入支付密码。");
            }

            var paymentRecord = this.paymentRecordData.Create(this.ChannelType, orderType.ToString(), subject, referenceNumber, 
                Math.Round(amount, 2), null, appType, osType, payer, $"{this.ChannelType}-{this.AppType}");
            return null;
            //try
            //{
            //    var wallet = this.walletData.GetWalletByUserId(userDto.UserId);

            //    if (wallet == null)
            //    {
            //        throw new Exception("用户钱包不存在。");
            //    }

            //    if (string.IsNullOrEmpty(wallet.PayPassword))
            //    {
            //        throw new ArgumentException("未设置支付密码，无法完成支付。");
            //    }

            //    if (wallet.PasswordErrorCount >= 3)
            //    {
            //        throw new ArgumentException("输入密码错误次数太多，请稍后再试。");
            //    }

            //    var user = this.userData.GetUserByUserId(userDto.UserId).Result;

            //    if (user?.AccountStatus != AccountStatus.Using)
            //    {
            //        throw new ArgumentException("用户不存在或者已禁用，无法使用余额支付。");
            //    }

            //    if (password.SHA256(user.Phone).ToUpperInvariant() != wallet.PayPassword.ToUpperInvariant())
            //    {
            //        this.walletData.UpdatePasswordErrorCount(user.UserId, wallet.PasswordErrorCount++);

            //        throw new ArgumentException("支付密码错误，请重试。");
            //    }

            //    if (wallet.Balance < paymentRecord.Amount)
            //    {
            //        throw new ArgumentException("您的可用余额不足，请充值后再试或者选择其他支付方式。");
            //    }

            //    // 更新钱包余额
            //    wallet.Balance = wallet.Balance - paymentRecord.Amount;

            //    // 钱包流水
            //    var userFlowRecord = new UserFlowRecordEntity
            //    {
            //        FlowType = orderType.ToString().AsEnum(FlowType.Other),
            //        OrderNo = referenceNumber,
            //        Balance = wallet.Balance,
            //        FlowDirection = SourceFlowDirectionType.Outlay,
            //        Amount = -paymentRecord.Amount,
            //        CreateTime = DateTime.Now,
            //        Remark = $"{orderType.GetDisplayString()} {paymentRecord.Amount:n2} 元",
            //        Channel = this.ChannelType,
            //        UserId = user.UserId,
            //        UserName = user.UserName
            //    };

            //    // 系统流水
            //    var systemFinanceFlowRecord = new SystemFinanceFlowRecordEntity
            //    {
            //        FlowType = orderType.ToString().AsEnum(FlowType.Other),
            //        OrderNo = referenceNumber,
            //        FlowDirection = SourceFlowDirectionType.Outlay,
            //        Amount = -paymentRecord.Amount,
            //        Remark = $"{orderType.GetDisplayString()} {paymentRecord.Amount:n2} 元",
            //        Channel = this.ChannelType,
            //        UserId = user.UserId,
            //        UserName = user.UserName
            //    };

            //    // TODO: 合并事务!
            //    this.walletData.Update(wallet);
            //    this.userFlowRecordData.AddAsync(userFlowRecord).GetAwaiter().GetResult();
            //    this.systemFinanceFlowRecordData.Append(systemFinanceFlowRecord).GetAwaiter().GetResult();

            //    // 支付成功
            //    paymentRecord = this.paymentRecordData.SetAsSuccess(paymentRecord.PaymentRecordId);

            //    string paymentRecordId = paymentRecord.PaymentRecordId.ToString();
            //    string _referenceNumber = referenceNumber;
            //    string originalAmount = amount.ToString();
            //    string discount = (amount - paymentRecord.Amount).ToString();
            //    string paidAmount = paymentRecord.Amount.ToString();
            //    string status = paymentRecord.Status;

            //    string arguments = $"PaymentRecordId={paymentRecordId}&ReferenceNumber={referenceNumber}&OriginalAmount={originalAmount}&Discount={discount}&PaidAmount={paidAmount}&Status={status}&Signature={HashHelper.SHA256(paymentRecordId + _referenceNumber + originalAmount + discount + paidAmount + status, SIGNATURE_SALT)}";

            //    PerformanceTracer.InvokeAsync(delegate
            //    {
            //        string callbackUrl = string.Format(configuration["PayNotify:postUrl"], this.ChannelType, userDto.UserId);

            //        try
            //        {
            //            new WebClient().DownloadString($"{callbackUrl}?{arguments}");
            //        }
            //        catch (Exception ex)
            //        {
            //            this.exceptionRecordService.InsertException(ex, JsonConvert.SerializeObject(paymentRecord));
            //        }
            //    });

            //    return paymentRecord.TransformTo<PaymentDto>(onAfter: (_, dest) =>
            //    {
            //        dest.Parameters = new Dictionary<string, string>
            //        {
            //            { "Balance", wallet.Balance.ToString() }
            //        };
            //        dest.ActionArguments = arguments;
            //    });
            //}
            //catch (Exception ex)
            //{
            //    this.paymentRecordData.SetAsFailed(paymentRecord.PaymentRecordId, ex.Message);

            //    throw;
            //}
        }

        public PaymentQRCodeDto GenerateQRCode(SourceAppType appType, FlowType orderType, string subject, string referenceNumber, SourceOSType osType, SourceDeviceType deviceType, string deviceId, IDictionary<string, string> parameters, decimal amount, SourcePayerType? payer, UserDto userDto)
        {
            throw new NotImplementedException();
        }

        public PaymentQRCodeDto AgentGenerateQRCode(SourceAppType appType, FlowType orderType, string subject,
string referenceNumber, SourceOSType osType, SourceDeviceType deviceType, string deviceId,
IDictionary<string, string> parameters, decimal amount, SourcePayerType? payer, AdminDto admin)
        {
            throw new NotImplementedException();
        }

        public decimal Refund(long adminId, string adminName, long paymentRecordId, decimal refundAmount)
        {
            PaymentRecordEntity payment = this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId);

            if (payment == null)
            {
                throw new ArgumentException("无法找到匹配的付款记录，无法进行退款。");
            }

            if (payment.UserId == null)
            {
                throw new ArgumentException("原始付款记录缺少用户信息，该笔交易无法退款。");
            }

            var user = this.userData.GetUserByUserId(payment.UserId.Value).Result;

            if (user == null)
            {
                throw new ArgumentException("原始付款记录用户已不存在，该笔交易无法退款。");
            }

            decimal refundedAmount = this.paymentRecordData.GetPaymentRecordsByReferenceNumber(payment.OrderType.AsEnum<FlowType>(), payment.ReferenceNumber).Where(p => p.Status == PaymentResultType.Success.ToString() && p.Amount < 0).Sum(p => p.Amount);

            if ((-refundedAmount + refundAmount) > payment.Amount)
            {
                throw new ArgumentException("可退余额不足，无法进行退款。");
            }

            decimal newAmount = Math.Round(refundAmount, 2);

            PaymentRecordEntity refund = this.paymentRecordData.Create(this.ChannelType, payment.OrderType.ToString(),"退款", payment.ReferenceNumber, -newAmount, user.UserId, payment.AppType.AsEnum<SourceAppType>(), payment.OSType.AsEnum<SourceOSType>(), (SourcePayerType)payment.Payer, $"{this.ChannelType}-{this.AppType}", adminId, adminName, PaymentOperationType.Refund.ToString(), SourceFlowDirectionType.Outlay);

            if (refund != null)
            {
                var wallet = this.walletData.GetWalletByUserId(user.UserId);

                // 更新钱包余额
                wallet.Balance = wallet.Balance - refund.Amount;

                // 钱包流水
                var userFlowRecord = new UserFlowRecordEntity
                {
                    FlowType = refund.OrderType.ToString().AsEnum(FlowType.Other),
                    OrderNo = refund.ReferenceNumber,
                    Balance = wallet.Balance,
                    FlowDirection = SourceFlowDirectionType.Income,
                    Amount = -refund.Amount,
                    CreateTime = DateTime.Now,
                    Remark = $"{refund.OrderType.GetDisplayString()} {refund.Amount:n2} 元",
                    Channel = this.ChannelType,
                    UserId = user.UserId,
                    UserName = user.UserName
                };

                // 系统流水
                var systemFinanceFlowRecord = new SystemFinanceFlowRecordEntity
                {
                    FlowType = refund.OrderType.ToString().AsEnum(FlowType.Other),
                    OrderNo = refund.ReferenceNumber,
                    FlowDirection = SourceFlowDirectionType.Income,
                    Amount = -refund.Amount,
                    Remark = $"{refund.OrderType.GetDisplayString()} {refund.Amount:n2} 元",
                    Channel = this.ChannelType,
                    UserId = user.UserId,
                    UserName = user.UserName
                };

                // TODO: 合并事务!
                this.walletData.Update(wallet);
                this.userFlowRecordData.AddAsync(userFlowRecord).GetAwaiter().GetResult();
                this.systemFinanceFlowRecordData.Append(systemFinanceFlowRecord).GetAwaiter().GetResult();

                this.paymentRecordData.SetAsSuccess(refund.PaymentRecordId);

                return payment.Amount - -refundedAmount - refundAmount;
            }
            else
            {
                this.paymentRecordData.SetAsFailed(refund.PaymentRecordId,string.Empty);

                throw new ArgumentException("将退款请求提交余额时发生错误，你可能需要先确认退款状态。");
            }
        }

        public ReceiveNotificationDto ReceiveNotification(IDictionary<string, string> parameters, out PaymentDto payment)
        {
            string paymentRecordId = parameters["PaymentRecordId"];
            string referenceNumber = parameters["ReferenceNumber"];
            string originalAmount = parameters["OriginalAmount"];
            string discount = parameters["Discount"];
            string paidAmount = parameters["PaidAmount"];
            string status = parameters["Status"];
            string signature = parameters["Signature"];

            if (HashHelper.SHA256(paymentRecordId + referenceNumber + originalAmount + discount + paidAmount + status, SIGNATURE_SALT) != signature)
            {
                throw new ArgumentException("验证签名失败。");
            }

            var paymentRecord = this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId.ToInt64());

            if (paymentRecord == null)
            {
                throw new ObjectNotFoundException();
            }

            payment = paymentRecord.TransformTo<PaymentDto>();

            return new ReceiveNotificationDto
            {
                StatusCode = 200,
                ContentType = "text/plain",
                ReturnString = "success"
            };
        }

        public decimal Transfer(SourceAppType appType, SourceOSType osType, long recordId, decimal transferAmount, long userId, string open_userid, UserDto userDto)
        {
            throw new NotImplementedException();
        }

        public IDictionary<string, string> CreateAppAuthLogin(SourceAppType appType, SourceOSType osType, UserDto userDto)
        {
            throw new NotImplementedException();
        }

        public IDictionary<string, object> AuthLoginInfo(string auth_code, SourceAppType appType, SourceOSType osType, UserDto userDto)
        {
            throw new NotImplementedException();
        }

        public bool ConfirmPayValidation(long paymentRecordId, string referenceNumber, string password,UserDto userDto)
        {
            PaymentRecordEntity paymentRecordEntity = this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId);
            if (paymentRecordEntity.Status == PaymentResultType.Success.ToString())
            {
                throw new ArgumentException("此订单已支付完成，请勿重复支付");
            }
            if (paymentRecordEntity.UserId != userDto.UserId)
            {
                throw new ArgumentException("请勿支付不属于您的订单");
            }
            if (paymentRecordEntity.ReferenceNumber != referenceNumber)
            {
                throw new ArgumentException("订单编号错误，请确认支付订单编号");
            }
            WalletDto walletDto = this.walletService.GetWalletByUserId(userDto.UserId, true);
            if (!walletDto.HasPayPwd)
            {
                throw new ArgumentException("未设置支付密码，无法完成支付");
            }
            if (walletDto.PasswordErrorCount == 3)
            {
                throw new ArgumentException("输入密码错误次数太多，请稍后再试");
            }
            if (!password.SHA256(userDto.Phone).Equals(walletDto.PayPassword))
            {
                this.walletService.UpdatePasswordErrorCount(userDto.UserId, walletDto.PasswordErrorCount++);
                throw new ArgumentException("支付密码错误，请重试");
            }
            if (walletDto.Balance < paymentRecordEntity.Amount)
            {
                throw new ArgumentException("您的可用余额不足，请核实后再试");
            }
            walletDto.Balance = walletDto.Balance - paymentRecordEntity.Amount;
            this.walletService.Update(walletDto);
            return true;
        }

        public PaymentRecordDto SyncCheckSign(IDictionary<string, string> parameters)
        {
            throw new NotImplementedException();
        }

        public PaymentRecordDto Synchronize(long paymentRecordId, PaymentSynchronizeRequestDto requestDto)
        {
            var entity = this.paymentRecordData.GetPaymentRecordByPaymentRecordId(paymentRecordId);

            if (entity == null)
            {
                throw new ObjectNotFoundException();
            }

            return entity.TransformTo<PaymentRecordDto>();
        }
    }
}
